#pragma once

#include "Log.hpp"
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

class Client
{
public:
    Client(const std::string& ip, uint16_t port)
        : _ip(ip)
        , _port(port)
        , _sock(-1)
    { }

    // 初始化客户端
    void init()
    {
        // 1. 创建网络套接字
        _sock = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sock < 0)
        {
            logMessage(FATAL, "%d:%s\n", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "网络套接字创建成功, _sock:%d\n", _sock);
  
        // 客户端程序不应由程序员显示给定ip和port来绑定
        // 而是应当在第一次发送信息的时候，由OS自动分配ip和端口号
    }

    // 客户端启动
    void start()
    {
        while(true)
        {
            // 1. 为接收客户端消息的服务端创建sockaddr结构体
            struct sockaddr_in sock_srv;  // 接受客户端消息的服务端数据
            memset(&sock_srv, 0, sizeof(sock_srv));  // 全部初始化为0
            sock_srv.sin_family = AF_INET;    // 网络协议族
            sock_srv.sin_addr.s_addr = inet_addr(_ip.c_str());   // 主机ip转网络ip
            sock_srv.sin_port = htons(_port);   // 端口号转为网络格式
            socklen_t len = sizeof(sock_srv);   // sockaddr_in 结构体长度

            std::string msg;   // 客户端发生的消息

            // 2. 发生信息
            std::cout << "请输入你要发送的信息: " << std::flush;
            std::getline(std::cin, msg);   // 按行读取
            
            sendto(_sock, msg.c_str(), msg.size(), 0, (const struct sockaddr *)&sock_srv, len);

            // 3.读取从服务端返回的消息
            struct sockaddr_in tmp;
            memset(&tmp, 0, sizeof(tmp));
            char buffer[1024];
            ssize_t n = recvfrom(_sock, (void*)buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&tmp, &len);
            if(n > 0)
            {
                buffer[n] = '\0';
                std::cout << buffer << std::endl;
            }
        }
    }

    ~Client()
    {
        if(_sock >= 0)
        {
            close(_sock);
        }
    }

private:
    std::string _ip;   // 客户端ip
    uint16_t _port;    // 端口号
    int _sock;         // 套接字对应文件描述符
};